package com.nantian.office.excel;


import com.nantian.common.util.CollectionUtils;
import com.nantian.common.util.StringUtils;
import com.nantian.exception.ExcelException;
import org.apache.commons.io.IOUtils;

import java.io.*;
import java.util.*;

/**
 * <p>ClassName: Excel</p>
 * <p>Description: </p>
 * <p>Copyright: Copyright©2014</p>
 * <p>Company: 广州南天电脑系统有限公司</p>
 * <p>Date: 2020-01-15</p>
 *
 * @author 刘晓辉
 * @version 1.0
 */
public class Excel {
    /**
     * 解析excel文件.
     * 支持xls、xlsx自动识别
     * map中的key存的是列索引，例如：第一列时，key为0
     * @param filePath 文件路径
     * @return 返回list数据
     * @throws ExcelException 解析异常
     */
    public static List<Map<String, Object>> parseObject(String filePath) throws ExcelException {
        if (StringUtils.isBlank(filePath)) {
            throw new ExcelException("待解析的文件路径不能为空");
        }
        return parseObject(filePath, 0);
    }

    /**
     * 解析excel文件.
     * 支持xls、xlsx自动识别
     * map中的key存的是列索引，例如：第一列时，key为0
     * 最大例为：int最大值
     * 最大行为：int最大值
     * @param filePath 文件路径
     * @param startRowIndex 从第几行开始读取数据
     * @return  返回list数据
     * @throws ExcelException 解析异常
     */
    public static List<Map<String, Object>> parseObject(String filePath, int startRowIndex) throws ExcelException {
        if (StringUtils.isBlank(filePath)) {
            throw new ExcelException("待解析的文件路径不能为空");
        }
        if (startRowIndex < 0) {
            throw new ExcelException("startRowInde不能小于0");
        }
        return ExcelUtils.parseObject(filePath, startRowIndex, Integer.MAX_VALUE, Integer.MAX_VALUE);
    }

    /**
     * 解析excel文件.
     * 支持xls、xlsx自动识别
     * map中的key存的是列索引，例如：第一列时，key为0
     * @param filePath 文件路径
     * @param startRowIndex 从第几行开始读取数据
     * @param maxRow 最大行
     * @param maxColumn 最大列
     * @return 返回list数据
     * @throws ExcelException 解析异常
     */
    public static List<Map<String, Object>> parseObject(String filePath, int startRowIndex, int maxRow, int maxColumn) throws ExcelException {
        if (StringUtils.isBlank(filePath)) {
            throw new ExcelException("待解析的文件路径不能为空");
        }
        if (startRowIndex < 0) {
            throw new ExcelException("startRowInde不能小于0");
        }
        return ExcelUtils.parseObject(filePath, startRowIndex, maxRow, maxColumn);
    }

    /**
     * 解析excel文件，并将数据转成bean对象
     * 支持xls、xlsx自动识别
     * @param filePath 文件路径
     * @param javaBean bean对象
     * @param <T> bean类型
     * @return 返回bean数据
     * @throws ExcelException 解析异常
     */
    public static <T>List<T> parseObject(String filePath, Class<T> javaBean) throws ExcelException {
        if (StringUtils.isBlank(filePath)) {
            throw new ExcelException("待解析的文件路径不能为空");
        }
        return parseObject(filePath, 0, javaBean);
    }

    /**
     * 解析excel文件，并将数据转成bean对象
     * 支持xls、xlsx自动识别
     * 不限最大行和最大例
     * @param filePath 文件路径
     * @param startRowIndex 从第几行开始读取数据
     * @param javaBean bean对象
     * @param <T> bean类型
     * @return 返回转换后的对象列表
     * @throws ExcelException 解析异常
     */
    public static <T>List<T> parseObject(String filePath, int startRowIndex, Class<T> javaBean) throws ExcelException {
        if (StringUtils.isBlank(filePath)) {
            throw new ExcelException("待解析的文件路径不能为空");
        }
        if (startRowIndex < 0) {
            throw new ExcelException("startRowInde不能小于0");
        }
        return ExcelUtils.parseObject(filePath, startRowIndex, javaBean, Integer.MAX_VALUE, Integer.MAX_VALUE);
    }

    /**
     * 解析excel文件，并将数据转成bean对象
     * 支持xls、xlsx自动识别
     * @param filePath 文件路径
     * @param startRowIndex 从第几行开始读取数据
     * @param maxRow 读取最大行数
     * @param maxColumn 读取最大列数
     * @param javaBean bean对象
     * @param <T> bean类型
     * @return 返回转换后的对象列表
     * @throws ExcelException 解析异常
     */
    public static <T>List<T> parseObject(String filePath, int startRowIndex, Class<T> javaBean, int maxRow, int maxColumn) throws ExcelException {
        if (StringUtils.isBlank(filePath)) {
            throw new ExcelException("待解析的文件路径不能为空");
        }
        if (startRowIndex < 0) {
            throw new ExcelException("startRowInde不能小于0");
        }
        return ExcelUtils.parseObject(filePath, startRowIndex, javaBean, maxRow, maxColumn);
    }

    /**
     * 将data数据写到指定的文件路径中
     * @param savePath 待写入的文件路径，非空
     * @param data 待写入的数据，非空
     * @throws ExcelException excel文件生成异常
     */
    public static void mapToExcelFile(String savePath, List<Map<String, Object>> data) throws ExcelException {
        mapToExcelFile(savePath, data, null);

    }
    /**
     * 将data数据写到指定的文件路径中
     * @param savePath 待写入的文件路径，非空
     * @param data 待写入的数据，非空
     * @param titles 列对应的标题，可为空，为空则按data中map集合的key顺序
     * @throws ExcelException excel文件生成异常
     */
    public static void mapToExcelFile(String savePath, List<Map<String, Object>> data, LinkedHashMap<String, String> titles) throws ExcelException {
        mapToExcelFile(savePath, data, titles, null);
    }
    /**
     * 将data数据写到指定的文件路径中
     * @param savePath 待写入的文件路径，非空
     * @param data 待写入的数据，非空
     * @param titles 列对应的标题，可为空，为空则按data中map集合的key顺序
     * @param columnIndex 列对应的下标位置，即列位置，可为空，为空默认data中map集合的顺序
     * @throws ExcelException excel文件生成异常
     */
    public static void mapToExcelFile(String savePath, List<Map<String, Object>> data, LinkedHashMap<String, String> titles, Map<String, Integer> columnIndex) throws ExcelException {
        mapToExcelFile(savePath, data, titles, columnIndex, null);

    }

    /**
     * 将data数据写到指定的文件路径中
     * @param savePath 待写入的文件路径，非空
     * @param data 待写入的数据，非空
     * @param titles 列对应的标题，可为空，为空则按data中map集合的key顺序
     * @param columnIndex 列对应的下标位置，即列位置，可为空，为空默认data中map集合的顺序
     * @param sheetName sheet 表的标题，可为空
     * @throws ExcelException excel文件生成异常
     */
    public static void mapToExcelFile(String savePath, List<Map<String, Object>> data, LinkedHashMap<String, String> titles, Map<String, Integer> columnIndex, String sheetName) throws ExcelException {
        mapToExcelFile(savePath, data, titles, columnIndex, sheetName, 0);
    }

    /**
     * 将data数据写到指定的文件路径中
     * @param savePath 待写入的文件路径，非空
     * @param data 待写入的数据，非空
     * @param titles 列对应的标题，可为空，为空则按data中map集合的key顺序
     * @param columnIndex 列对应的下标位置，即列位置，可为空，为空默认data中map集合的顺序
     * @param sheetName sheet 表的标题，可为空
     * @param startRowIndex 从第几行开始写，可为空，为空默认第一行开始
     * @throws ExcelException excel文件生成异常
     */
    public static void mapToExcelFile(String savePath, List<Map<String, Object>> data, LinkedHashMap<String, String> titles, Map<String, Integer> columnIndex, String sheetName, int startRowIndex) throws ExcelException {
        if (CollectionUtils.isEmpty(data)) {
            return;
        }
        if (StringUtils.isBlank(savePath)) {
            throw new ExcelException("保存的文件路径不能为空");
        }
        if (startRowIndex < 0) {
            throw new ExcelException("起始行不能小于0");
        }
        FileOutputStream outputStream = null;
        try {
            outputStream = new FileOutputStream(new File(savePath));
            boolean hasTitle = false;
            if (titles != null && titles.size() > 0) {
                hasTitle = true;
            }
            ExcelUtils.mapToExcelFile(outputStream, null, null, startRowIndex, data, hasTitle, sheetName, titles, columnIndex);
        } catch (FileNotFoundException e) {
            throw new ExcelException("指定的文件路径不存在：" + savePath);
        } finally {
            IOUtils.closeQuietly(outputStream);
        }
    }
    /**
     * 将data数据写到指定的文件路径中
     * @param savePath 待写入的文件路径，非空
     * @param templateFile 模板文件
     * @param data 待写入的数据，非空
     * @param titles 列对应的标题，可为空，为空则按data中map集合的key顺序
     * @param columnIndex 列对应的下标位置，即列位置，可为空，为空默认data中map集合的顺序
     * @param sheetName sheet 表的标题，可为空
     * @param startRowIndex 从第几行开始写，可为空，为空默认第一行开始
     * @throws ExcelException excel文件生成异常
     */
    public static void mapToExcelFile(String savePath, String templateFile, List<Map<String, Object>> data, LinkedHashMap<String, String> titles, Map<String, Integer> columnIndex, String sheetName, int startRowIndex) throws ExcelException {
        if (CollectionUtils.isEmpty(data)) {
            return;
        }
        if (StringUtils.isBlank(savePath)) {
            throw new ExcelException("保存的文件路径不能为空");
        }
        if (StringUtils.isBlank(templateFile)) {
            throw new ExcelException("模板文件路径不能为空");
        }
        if (startRowIndex < 0) {
            throw new ExcelException("起始行不能小于0");
        }
        FileOutputStream outputStream = null;
        FileInputStream in = null;
        try {
            outputStream = new FileOutputStream(new File(savePath));
            in = new FileInputStream(new File(templateFile));
            boolean hasTitle = false;
            if (titles != null && titles.size() > 0) {
                hasTitle = true;
            }
            ExcelEnum type = ExcelUtils.getExcelType(templateFile);
            ExcelUtils.mapToExcelFile(outputStream, in, type, startRowIndex, data, hasTitle, sheetName, titles, columnIndex);
        } catch (FileNotFoundException e) {
            throw new ExcelException("指定的文件路径不存在：" + savePath);
        } finally {
            IOUtils.closeQuietly(outputStream);
            IOUtils.closeQuietly(in);
        }
    }


    /**
     * 将data数据写到指定的文件路径中
     * @param out 输出流，非空
     * @param data 待写入的数据，非空
     * @throws ExcelException excel文件生成异常
     */
    public static void mapToExcelFile(OutputStream out, List<Map<String, Object>> data) throws ExcelException {
        mapToExcelFile(out, data, null);

    }
    /**
     * 将data数据写到指定的文件路径中
     * @param out 输出流，非空
     * @param data 待写入的数据，非空
     * @param titles 列对应的标题，可为空，为空则按data中map集合的key顺序
     * @throws ExcelException excel文件生成异常
     */
    public static void mapToExcelFile(OutputStream out, List<Map<String, Object>> data, LinkedHashMap<String, String> titles) throws ExcelException {
        mapToExcelFile(out, data, titles, null);
    }
    /**
     * 将data数据写到指定的文件路径中
     * @param out 输出流，非空
     * @param data 待写入的数据，非空
     * @param titles 列对应的标题，可为空，为空则按data中map集合的key顺序
     * @param columnIndex 列对应的下标位置，即列位置，可为空，为空默认data中map集合的顺序
     * @throws ExcelException excel文件生成异常
     */
    public static void mapToExcelFile(OutputStream out, List<Map<String, Object>> data, LinkedHashMap<String, String> titles, Map<String, Integer> columnIndex) throws ExcelException {
        mapToExcelFile(out, data, titles, columnIndex, null);

    }

    /**
     * 将data数据写到指定的文件路径中
     * @param out 输出流，非空
     * @param data 待写入的数据，非空
     * @param titles 列对应的标题，可为空，为空则按data中map集合的key顺序
     * @param columnIndex 列对应的下标位置，即列位置，可为空，为空默认data中map集合的顺序
     * @param sheetName sheet 表的标题，可为空
     * @throws ExcelException excel文件生成异常
     */
    public static void mapToExcelFile(OutputStream out, List<Map<String, Object>> data, LinkedHashMap<String, String> titles, Map<String, Integer> columnIndex, String sheetName) throws ExcelException {
        mapToExcelFile(out, data, titles, columnIndex, sheetName, 0);
    }

    /**
     * 将data数据写到指定的文件路径中
     * @param out 输出流，非空
     * @param data 待写入的数据，非空
     * @param titles 列对应的标题，可为空，为空则按data中map集合的key顺序
     * @param columnIndex 列对应的下标位置，即列位置，可为空，为空默认data中map集合的顺序
     * @param sheetName sheet 表的标题，可为空
     * @param startRowIndex 从第几行开始写，可为空，为空默认第一行开始
     * @throws ExcelException excel文件生成异常
     */
    public static void mapToExcelFile(OutputStream out, List<Map<String, Object>> data, LinkedHashMap<String, String> titles, Map<String, Integer> columnIndex, String sheetName, int startRowIndex) throws ExcelException {
        if (CollectionUtils.isEmpty(data)) {
            return;
        }
        if (out == null) {
            throw new ExcelException("保存的文件流不能为空");
        }
        if (startRowIndex < 0) {
            throw new ExcelException("起始行不能小于0");
        }
        boolean hasTitle = false;
        if (titles != null && titles.size() > 0) {
            hasTitle = true;
        }
        ExcelUtils.mapToExcelFile(out, null, null, startRowIndex, data, hasTitle, sheetName, titles, columnIndex);
    }
    /**
     * 将data数据写到指定的文件路径中
     * @param out 输出流，非空
     * @param templateFile 模板文件
     * @param data 待写入的数据，非空
     * @param titles 列对应的标题，可为空，为空则按data中map集合的key顺序
     * @param columnIndex 列对应的下标位置，即列位置，可为空，为空默认data中map集合的顺序
     * @param sheetName sheet 表的标题，可为空
     * @param startRowIndex 从第几行开始写，可为空，为空默认第一行开始
     * @throws ExcelException excel文件生成异常
     */
    public static void mapToExcelFile(OutputStream out, String templateFile, List<Map<String, Object>> data, LinkedHashMap<String, String> titles, Map<String, Integer> columnIndex, String sheetName, int startRowIndex) throws ExcelException {
        if (CollectionUtils.isEmpty(data)) {
            return;
        }
        if (out == null) {
            throw new ExcelException("保存的文件流不能为空");
        }
        if (StringUtils.isBlank(templateFile)) {
            throw new ExcelException("模板文件流不能为空");
        }
        if (startRowIndex < 0) {
            throw new ExcelException("起始行不能小于0");
        }
        boolean hasTitle = false;
        if (titles != null && titles.size() > 0) {
            hasTitle = true;
        }
        FileInputStream in = null;
        try {
            in = new FileInputStream(new File(templateFile));
            ExcelEnum type = ExcelUtils.getExcelType(templateFile);
            ExcelUtils.mapToExcelFile(out, in, type, startRowIndex, data, hasTitle, sheetName, titles, columnIndex);
        } catch (FileNotFoundException e) {
            throw new ExcelException("指定的文件路径不存在：" + templateFile);
        } finally {
            IOUtils.closeQuietly(in);
        }
    }


    /**
     * 将java bean对象，存到excel文件中。
     * java bean对象中，需要写入文件的字段，都必需使用@Cell注解声明，
     * 如果需要在sheet名称，可以在该类上使用@sheet注解进行标注。
     * @param savePath 待写入的文件路径，非空
     * @param data 待写入的数据，非空
     */
    public static void objectToExcelFile(String savePath, List<?> data) {
        objectToExcelFile(savePath, data, false);
    }
    /**
     * 将java bean对象，存到excel文件中。
     * java bean对象中，需要写入文件的字段，都必需使用@Cell注解声明，
     * 如果需要在sheet名称，可以在该类上使用@sheet注解进行标注。
     * @param savePath 待写入的文件路径，非空
     * @param data 待写入的数据，非空
     * @param needTitle 是否需要写入标题
     */
    public static void objectToExcelFile(String savePath, List<?> data, boolean needTitle) {
        objectToExcelFile(savePath, data, needTitle, 0);
    }
    /**
     * 将java bean对象，存到excel文件中。
     * java bean对象中，需要写入文件的字段，都必需使用@Cell注解声明，
     * 如果需要在sheet名称，可以在该类上使用@sheet注解进行标注。
     * @param savePath 待写入的文件路径，非空
     * @param data 待写入的数据，非空
     * @param needTitle 是否需要写入标题，可为空，为空时，按java bean的字段名为标题
     * @param startRowIndex 从第几行开始写入，可为空，默认从0行开始写入
     */
    public static void objectToExcelFile(String savePath, List<?> data, boolean needTitle, int startRowIndex) {
        if (CollectionUtils.isEmpty(data)) {
            return;
        }
        if (StringUtils.isBlank(savePath)) {
            throw new ExcelException("保存的文件路径不能为空");
        }
        if (startRowIndex < 0) {
            throw new ExcelException("起始行不能小于0");
        }
        FileOutputStream outputStream = null;
        try {
            outputStream = new FileOutputStream(new File(savePath));
            ExcelUtils.objectToExcelFile(outputStream, null, null, startRowIndex, data, needTitle);
        } catch (FileNotFoundException e) {
            throw new ExcelException("指定的文件路径不存在：" + savePath);
        } finally {
            IOUtils.closeQuietly(outputStream);
        }

    }

    /**
     * 将java bean对象，存到excel文件中。
     * java bean对象中，需要写入文件的字段，都必需使用@Cell注解声明，
     * 如果需要在sheet名称，可以在该类上使用@sheet注解进行标注。
     * @param savePath 待写入的文件路径，非空
     * @param templateFile excel模板文件
     * @param data 待写入的数据，非空
     * @param needTitle 是否需要写入标题，可为空，为空时，按java bean的字段名为标题
     * @param startRowIndex 从第几行开始写入，可为空，默认从0行开始写入
     */
    public static void objectToExcelFile(String savePath, String templateFile, List<?> data, boolean needTitle, int startRowIndex) {
        if (CollectionUtils.isEmpty(data)) {
            return;
        }
        if (StringUtils.isBlank(savePath)) {
            throw new ExcelException("保存的文件路径不能为空");
        }
        if (StringUtils.isBlank(templateFile)) {
            throw new ExcelException("模板文件路径不能为空");
        }
        if (startRowIndex < 0) {
            throw new ExcelException("起始行不能小于0");
        }
        FileOutputStream outputStream = null;
        FileInputStream in = null;
        try {
            outputStream = new FileOutputStream(new File(savePath));
            in = new FileInputStream(new File(templateFile));
            ExcelEnum excelEnum = ExcelUtils.getExcelType(templateFile);
            ExcelUtils.objectToExcelFile(outputStream, in, excelEnum, startRowIndex, data, needTitle);
        } catch (FileNotFoundException e) {
            throw new ExcelException("指定的文件路径不存在：" + savePath);
        } finally {
            IOUtils.closeQuietly(outputStream);
            IOUtils.closeQuietly(in);
        }

    }





    /**
     * 将java bean对象，存到excel文件中。
     * java bean对象中，需要写入文件的字段，都必需使用@Cell注解声明，
     * 如果需要在sheet名称，可以在该类上使用@sheet注解进行标注。
     * @param out 文件输出流，非空
     * @param data 待写入的数据，非空
     */
    public static void objectToExcelFile(OutputStream out, List<?> data) {
        objectToExcelFile(out, data, false);
    }
    /**
     * 将java bean对象，存到excel文件中。
     * java bean对象中，需要写入文件的字段，都必需使用@Cell注解声明，
     * 如果需要在sheet名称，可以在该类上使用@sheet注解进行标注。
     * @param out 文件输出流，非空
     * @param data 待写入的数据，非空
     * @param needTitle 是否需要写入标题
     */
    public static void objectToExcelFile(OutputStream out, List<?> data, boolean needTitle) {
        objectToExcelFile(out, data, needTitle, 0);
    }
    /**
     * 将java bean对象，存到excel文件中。
     * java bean对象中，需要写入文件的字段，都必需使用@Cell注解声明，
     * 如果需要在sheet名称，可以在该类上使用@sheet注解进行标注。
     * @param out 文件输出流，非空
     * @param data 待写入的数据，非空
     * @param needTitle 是否需要写入标题，可为空，为空时，按java bean的字段名为标题
     * @param startRowIndex 从第几行开始写入，可为空，默认从0行开始写入
     */
    public static void objectToExcelFile(OutputStream out, List<?> data, boolean needTitle, int startRowIndex) {
        if (CollectionUtils.isEmpty(data)) {
            return;
        }
        if (out == null) {
            throw new ExcelException("保存的文件输出流不能为空");
        }
        if (startRowIndex < 0) {
            throw new ExcelException("起始行不能小于0");
        }
        ExcelUtils.objectToExcelFile(out, null, null, startRowIndex, data, needTitle);

    }

    public static void objectToExcelFile(OutputStream out, String templateFile, List<?> data, boolean needTitle, int startRowIndex) {
        if (CollectionUtils.isEmpty(data)) {
            return;
        }
        if (out == null) {
            throw new ExcelException("保存的文件输出流不能为空");
        }
        if (StringUtils.isBlank(templateFile)) {
            throw new ExcelException("模板文件输出流不能为空");
        }
        if (startRowIndex < 0) {
            throw new ExcelException("起始行不能小于0");
        }
        FileInputStream in = null;
        try {
            in = new FileInputStream(new File(templateFile));
            ExcelEnum type = ExcelUtils.getExcelType(templateFile);
            ExcelUtils.objectToExcelFile(out, in, type, startRowIndex, data, needTitle);
        } catch (FileNotFoundException e) {
            throw new ExcelException("指定的文件路径不存在：" + templateFile);
        } finally {
            IOUtils.closeQuietly(in);
        }
    }



}